#include "t0022-0.h"
#include "cui.h"

#define MODNAME     "[CFORM]"
struct cui_widget_operations __form_ops;
static void destroy_fields(struct cui_form_st* cform);

int cui_form_parse(cJSON *root, struct cui_widget_st* widget)
{
    int retval = -1;
    cJSON *fieldlist;
    struct cui_form_st *cform = zmalloc(sizeof(struct cui_form_st));
    ERRSYS_INFOPRINT("### parsing FORM ...\n");

    if (cform == NULL) {
        goto err;
    }
    retval = cui_attributes_parse(root, widget, &__form_ops, cform);
    if (retval == -1) {
        goto err;
    }
    // 设置widget类型
    SET_WIDGET_TYPE(widget, FORM);
    
    fieldlist = cJSON_GetObjectItem(root, CUI_CONFIELD_FIELDS);
    if (cJSON_ValidArray(fieldlist)) {
        int nfields = cJSON_GetArraySize(fieldlist);
        ERRSYS_INFOPRINT("FORM %s total %d fields\n", widget->name, nfields);
        if (nfields <= CUI_FIELDITEMS_MAX) {
            int i;
            cform->nfields = nfields;
            cform->cfields = zmalloc(sizeof(struct cui_field_st) * nfields);
            if (cform->cfields) {
                for (i = 0;i < nfields;i ++) {
                    cJSON *field;
                    cJSON *node, *optlist;
                    struct cui_field_st *cfield;

                    cfield = &cform->cfields[i];
                    field = cJSON_GetArrayItem(fieldlist, i);
                    node = cJSON_GetObjectItem(field, CUI_CONFIELD_NAME);
                    if (cJSON_ValidString(node)) {
                        strncpy_safe(cfield->name, node->valuestring, CUI_WIDGETTITLE_BUFLEN);
                    }
                    node = cJSON_GetObjectItem(field, CUI_CONFIELD_LINES);
                    if (cJSON_ValidNumber(node)) {
                        cfield->height = node->valueint;
                    }
                    node = cJSON_GetObjectItem(field, CUI_CONFIELD_COLS);
                    if (cJSON_ValidNumber(node)) {
                        cfield->width_field = node->valueint;
                    }
                    cfield->width_label = strlen(cfield->name);
                    cfield->beginx_label = 0;
                    cfield->beginx_field = cfield->beginx_label + cfield->width_label;
                    cfield->beginy_label = i;
                    cfield->beginy_field = cfield->beginy_label;

                    ERRSYS_INFOPRINT("FORM %s height %d labelwidth %d fieldwith %d x %d y %d\n", widget->name, cfield->height, cfield->width_label, cfield->width_field, cfield->beginx_field, cfield->beginy_field);
                    /* options */
                    optlist = cJSON_GetObjectItem(field, CUI_CONFIELD_OPTIONS);
                    if (cJSON_ValidArray(optlist)) {
                        int j;
                        int nopts = cJSON_GetArraySize(optlist);
                        for (j = 0;j < nopts;j ++) {
                            cJSON *opt = cJSON_GetArrayItem(optlist, j);
                            if (cJSON_ValidString(opt)) {
                                if (is_string_equal_safe(opt->valuestring, CUI_CONFIELD_BACKGROUND_UNDERLINE)) {
                                    cfield->options |= FIELD_FLAGS_BACKGROUND_UNDERLINE;
                                    ERRSYS_INFOPRINT("FORM %s background underline\n", widget->name);
                                }
                                else if (is_string_equal_safe(opt->valuestring, CUI_CONFIELD_OPTION_AUTOSKIP)) {
                                    cfield->options |= FIELD_FLAGS_OPTIONS_AUTOSKIP;
                                    ERRSYS_INFOPRINT("FORM %s options autoskip\n", widget->name);
                                }
                            }
                        }
                    }
                }
            }
            else {
                cform->nfields = 0;
                ERRSYS_INFOPRINT("Fail to malloc %d \"struct cui_field_st\"\n", nfields);
            }
        }
    }
err:
    if (retval != 0) {
        free(cform);
    }
    return retval;
}

int cui_form_keyevent(struct cui_widget_st* widget, int key)
{
    int handled = CUI_WIDGETEVENT_PROPAGATION;
    ERRSYS_INFOPRINT("%s:%s %s(%d) \n", widget->name, __func__, cui_key_str(key), key);

    if (widget != NULL) {
        struct cui_form_st *cform = widget->widgetinstance;

        ERRSYS_INFOPRINT("%s:%s %s(%d) \n", widget->name, __func__, cui_key_str(key), key);

        if (!IS_WIDGET_KEYEVENTOFF(widget)) {
            /* default handler */
            switch(key) {
                case KEY_DOWN:
                    form_driver(cform->form, REQ_NEXT_FIELD);
                    break;
                case KEY_UP:
                    form_driver(cform->form, REQ_NEXT_FIELD);
                    break;
                case KEY_NPAGE:
                    form_driver(cform->form, REQ_NEXT_FIELD);
                    break;
                case KEY_PPAGE:
                    form_driver(cform->form, REQ_NEXT_FIELD);
                    break;
				case KEY_DC:
                    ERRSYS_INFOPRINT("KEY_DC\n");
					form_driver(cform->form, REQ_DEL_CHAR);
                    break;
				case KEY_BACKSPACE:
				case KEY_BACKSPACE_US:
                    ERRSYS_INFOPRINT("KEY_BACKSPACE_US\n");
					form_driver(cform->form, REQ_DEL_PREV);
					// form_driver(cform->form, REQ_END_FIELD);
                    break;
                default:
                    form_driver(cform->form, key);
                    form_driver(cform->form, REQ_VALIDATION);
                    break;
            }
        }
        else {
            ERRSYS_INFOPRINT("%s:keyevent is turned off\n", widget->name);
        }
        
        /* customized handler */
        if (widget->cb.keyevent != NULL) {
            handled = widget->cb.keyevent(widget, key);
        }
    }

    return handled;
}

static FIELD* create_field(struct cui_field_st *cfield)
{
    FIELD *field = NULL;
    // ERRSYS_INFOPRINT("%s cfield %p\n", __func__, cfield);
    if (cfield) {
        ERRSYS_INFOPRINT("%s field\"%s\" y %d x %d w %d h %d\n", __func__, cfield->name, cfield->beginy_label, cfield->beginx_label, cfield->width_field, cfield->height);
        // FIELD *new_field(int height, int width, int toprow, int leftcol, int offscreen, int nbuffers);
        field = new_field(cfield->height, cfield->width_field, cfield->beginy_field, cfield->beginx_field, 0, 0);
        if (field == NULL) {
            ERRSYS_INFOPRINT("Fail to create field for form, %s\n", cui_strerror(errno));
        }
        if (cfield->options & FIELD_FLAGS_BACKGROUND_UNDERLINE) {
            set_field_back(field, A_UNDERLINE);
            ERRSYS_INFOPRINT("FIELD[%s] option A_UNDERLINE\n", cfield->name);
        }
        
        if (cfield->options & FIELD_FLAGS_OPTIONS_AUTOSKIP) {
            field_opts_off(field, O_AUTOSKIP);
            ERRSYS_INFOPRINT("FIELD[%s] option O_AUTOSKIP\n", cfield->name);
        }
        // set_field_buffer(field, 0, "hello");
    }
    // ERRSYS_INFOPRINT("%s field %p end\n", __func__, field);
    return field;
}

void cui_initialize_dummy_field(struct cui_field_st *cfield)
{
    memset(cfield, 0, sizeof(struct cui_field_st));
    snprintf(cfield->name, CUI_WIDGETTITLE_BUFLEN, "dummy");
    cfield->height = 1;
    cfield->beginx_label = 1;
    cfield->beginy_label = 1;
    cfield->beginx_field = 8;
    cfield->beginy_field = 1;
    cfield->width_field = 10;
    cfield->width_label = 8;
    cfield->options = FIELD_FLAGS_BACKGROUND_UNDERLINE | FIELD_FLAGS_OPTIONS_AUTOSKIP;
}

static int create_form(struct cui_widget_st *widget)
{
    int retval = -1;
    struct cui_form_st* cform = NULL;
    if (widget) {
        cform = widget->widgetinstance;
        if (cform) {
            if (cform->nfields) {
                cform->fields = zmalloc(sizeof(FIELD*) * (cform->nfields + 1));
                ERRSYS_INFOPRINT("%s malloc %d fields %p\n", __func__, cform->nfields, cform->fields); 
                if (cform->fields) {
                    int i;
                    int rows, cols;
                    for (i = 0;i < cform->nfields;i ++) {
                        struct cui_field_st *cfield = &cform->cfields[i];
                        cform->fields[i] = create_field(cfield);
                        if (cform->fields[i] == NULL) {
                            goto err1;
                        }
                    }
                    cform->form = new_form(cform->fields);
                    if (cform->form == NULL) {
                        goto err2;
                    }
                    scale_form(cform->form, &rows, &cols);
                    if (rows > widget->lines) {
                        widget->lines = rows;
                    }
                    if (cols > widget->cols) {
                        widget->cols = cols;
                    }
                    widget->win = newwin(widget->lines, widget->cols, widget->beginy, widget->beginx);

                    set_form_win(cform->form, widget->win);
                    // WINDOW *derwin(WINDOW *orig,int nlines, int ncols,int begin_y, int begin_x);
                    set_form_sub(cform->form, derwin(widget->win, rows, cols, 0, 0));
                    widget->flags |= CUI_WIDGET_FLAGS_INPUTEVENT;
                    retval = 0;
                }
                else {
                    ERRSYS_INFOPRINT("Fail to malloc %d field for form [%s]\n", cform->nfields, widget->name);
                }
            }
        }
    }

err2:
    if (retval < 0) {
        cui_form_destroy(widget);
    }
err1:
    if (retval < 0) {
        destroy_fields(cform);
    }

    return retval;
}

/* 动态创建form的field条目
 * form_rows, form_cols,表示form几行几列
 */
static int cui_fields_initialize(struct cui_widget_st *widget, int nitems, const char **name, struct cui_field_st *template)
{
    int retval = -1;
    struct cui_form_st *cform = NULL;
    ERRSYS_INFOPRINT("%s\n", __func__);
    if (widget && template && name) {
        if (IS_WIDGET_TYPE(widget, FORM)) {
            if (nitems != 0) {
                int i;
                cform = widget->widgetinstance;
                cform->fields = zmalloc(sizeof(FIELD*) * (nitems + 1));
                cform->cfields = zmalloc(sizeof(struct cui_field_st) * nitems);
                // ERRSYS_INFOPRINT("%s %d fields %p\n", __func__, nitems, cform->fields); 
                // ERRSYS_INFOPRINT("%s cfields %p\n", __func__, cform->cfields); 
                if (cform->fields && cform->cfields) {
                    int namelength = 0;
                    int beginy_field_last = 0;
                    for (i = 0;i < nitems;i ++) {
                        int n;
                        memcpy(&cform->cfields[i], template, sizeof(struct cui_field_st));
                        /* assign field name */
                        strncpy_safe(cform->cfields[i].name, name[i], CUI_WIDGETTITLE_BUFLEN);
                        /* adjust field y */
                        cform->cfields[i].beginy_field = cform->cfields[0].beginy_field + i;
                        /* save field label name length */
                        n = strlen(cform->cfields[i].name);
                        namelength = n > namelength ? n : namelength;
                    }

                    for (i = 0;i < nitems;i ++) {
                        cform->cfields[i].width_label = namelength;
                        cform->cfields[i].width_field = cform->cfields[i].width_label;
                        cform->cfields[i].beginx_label = 0;
                        cform->cfields[i].beginx_field = cform->cfields[i].beginx_label + namelength;

                        cform->cfields[i].beginy_label = beginy_field_last;
                        cform->cfields[i].beginy_field = beginy_field_last;
                        beginy_field_last += 2; // empty line

                        cform->cfields[i].options = template->options;
                        cform->fields[i] = create_field(&cform->cfields[i]);
                        if (cform->fields[i] == NULL) {
                            goto err1;
                        }
                    }
                    cform->nfields = nitems;
                    cform->form = new_form(cform->fields);
                    retval = 0;
                }
                else {
                    if (cform->fields == NULL) {
                        ERRSYS_INFOPRINT("Fail to malloc %d FIELD* for form [%s]\n", cform->nfields + 1, widget->name);
                    }
                    if (cform->cfields == NULL) {
                        ERRSYS_INFOPRINT("Fail to malloc %d struct cui_field_st for form [%s]\n", cform->nfields, widget->name);
                    }
                }
            }
            else {
                ERRSYS_INFOPRINT("Can not create nitems(%d) field\n", nitems);
            }
        }
    }

err1:
    if (retval < 0) {
        destroy_fields(cform);
    }

    return retval;
}

static int cui_field_maxcols(struct cui_widget_st *widget)
{
    int maxcols = -1;
    if (widget != NULL && widget->widgetinstance != NULL) {
        struct cui_form_st *cform = widget->widgetinstance;
        if (cform != NULL) {
            int i;
            for (i = 0;i < cform->nfields;i ++) {
                int cols = cform->cfields[i].width_field + cform->cfields[i].width_label;
                if (maxcols < cols) {
                    maxcols = cols;
                }
            }
            if (strlen(widget->title) > maxcols) {
                maxcols = strlen(widget->title);
            }
            ERRSYS_INFOPRINT("maxcols = %d %d\n", __func__, cform->cfields[0].width_field + cform->cfields[0].width_label);
        }
    }
    return maxcols;
}

static int cui_field_rows(struct cui_widget_st *widget)
{
    int rows = -1;
    if (widget != NULL && widget->widgetinstance != NULL) {
        struct cui_form_st *cform = widget->widgetinstance;
        if (cform != NULL) {
            int i;
            rows = 0;
            for (i = 0;i < cform->nfields;i ++) {
                rows += cform->cfields[i].height;   // '1' is empty line
            }
            rows += cform->nfields - 1; // plus all empty line
        }
    }

    return rows;
}

int cui_field_setvalue(struct cui_widget_st *widget, int fieldno, const char* value)
{
    if (widget != NULL && widget->widgetinstance != NULL) {
        struct cui_form_st *cform = widget->widgetinstance;
        if (cform != NULL && fieldno < cform->nfields) {
            set_field_buffer(cform->fields[fieldno], 0, value);
            return 0;
        }
    }
    return -1;
}

void cui_field_setoptions(struct cui_widget_st *widget, int fieldno, int options, int on)
{
    if (widget != NULL && widget->widgetinstance != NULL) {
        struct cui_form_st *cform = widget->widgetinstance;
        if (cform != NULL && fieldno < cform->nfields) {
            if (on) {
                field_opts_on(cform->fields[fieldno], options);
            }
            else {
                field_opts_off(cform->fields[fieldno], options);
            }
        }
    }
}

void cui_field_setfocus(struct cui_widget_st *widget, int fieldno)
{
    if (widget != NULL && widget->widgetinstance != NULL) {
        struct cui_form_st *cform = widget->widgetinstance;
        if (cform != NULL && fieldno < cform->nfields) {
            set_current_field(cform->form, cform->fields[fieldno]);
        }
    }
}

int cui_form_initialize(struct cui_widget_st *widget, int nitems, const char **name, struct cui_field_st *template)
{
    int retval = -1;
    struct cui_form_st *cform = NULL;

    ERRSYS_INFOPRINT("%s\n", __func__);
    cui_form_reset(widget);
    if (widget->widgetinstance == NULL) {
        widget->widgetinstance = zmalloc(sizeof(struct cui_form_st));
        if (widget->widgetinstance == NULL) {
            ERRSYS_INFOPRINT("Fail to malloc cui_form_st for widget [%s]", widget->name);
            goto out;
        }
    }

    retval = cui_fields_initialize(widget, nitems, name, template);
    if (retval == 0) {
        retval = -1;
        cform = widget->widgetinstance;
        if (cform && cform->nfields != 0) {
            int cols = cui_field_maxcols(widget);
            int rows = cui_field_rows(widget);
            // int cols = cform->cfields[0].width_field + cform->cfields[0].width_label;
            // int rows = cform->cfields[0].height;
            int form_beginx = 2;
            int form_beginy = 2;
            // scale_form(cform->form, &rows, &cols);
            // widget->win = newwin(rows + 4, cols + 4, widget->beginy, widget->beginx);
            widget->lines = rows + CUI_WIDGET_BORDER_WIDTHX2 + CUI_WIDGET_BORDER_MARGIN * 2;
            widget->cols = cols + CUI_WIDGET_BORDER_WIDTHX2  + CUI_WIDGET_BORDER_MARGIN * 2;
            if (IS_WIDGET_TITLE(widget)) {
                widget->lines += CUI_WIDGET_TITLESPLITTER_WIDTH + CUI_WIDGET_TITLE_HEIGHT;  // banner and splitter width
                form_beginy += CUI_WIDGET_TITLESPLITTER_WIDTH + CUI_WIDGET_TITLE_HEIGHT;
            }
            wresize(widget->win, widget->lines, widget->cols);
            mvwin(widget->win, widget->beginy, widget->beginx);
            // TODO: field的坐标基于derwin，而label的print基于widget
            // WINDOW *derwin(WINDOW *orig, int nlines, int ncols, int begin_y, int begin_x);
            set_form_win(cform->form, widget->win);
            set_form_sub(cform->form, cform->win = derwin(widget->win, rows, cols, form_beginy, form_beginx));
            retval = 0;
        }
    }

    
out:
    ERRSYS_INFOPRINT("%s end\n", __func__);
    return retval;
}

/* 根据cui_form_parse的配置，创建form */
int cui_form_create(struct cui_widget_st *widget)
{
    ERRSYS_INFOPRINT("[%s] create form\n", widget->name);
    if (widget != NULL) {
        widget->win = newwin(widget->lines, widget->cols, widget->beginy, widget->beginx);
        return create_form(widget);
    }

    return -1;
}

static void destroy_fields(struct cui_form_st* cform)
{
    ERRSYS_INFOPRINT("%s cform %p\n", __func__, cform); 
    if (cform) {
        int i;
        // ERRSYS_INFOPRINT("%s nfields %d\n", __func__, cform->nfields); 
        for (i = 0;i < cform->nfields;i ++) {
            free_field(cform->fields[i]);
        }
        cform->nfields = 0;
        if (cform->fields) {
            free(cform->fields);
            cform->fields = NULL;
        }
        // ERRSYS_INFOPRINT("%s free cfields %p\n", __func__, cform->cfields); 
        if (cform->cfields) {
            free(cform->cfields);
            cform->cfields = NULL;
        }

    }
    // ERRSYS_INFOPRINT("%s cform %p end\n", __func__, cform); 
}

void cui_form_reset(struct cui_widget_st *widget)
{
    ERRSYS_INFOPRINT("%s\n", __func__);
    if (widget) {
        struct cui_form_st* cform = ((struct cui_widget_st*)widget)->widgetinstance;
        if (cform && cform->form) {
            unpost_form(cform->form);
            free_form(cform->form);
            cform->form = NULL;
        }
        destroy_fields(cform);
    }
}

void cui_form_destroy(struct cui_widget_st *widget)
{
    ERRSYS_INFOPRINT("%s\n", __func__);
    if (widget) {
        cui_form_reset(widget);
        free(widget->widgetinstance);
        widget->widgetinstance = NULL;
    }
}

void cui_form_paint(struct cui_widget_st *widget)
{
    ERRSYS_INFOPRINT("!!!!!! %s\n", __func__);
    if (widget) {
        struct cui_form_st* cform = widget->widgetinstance;
        WINDOW *win = widget->win;
        int w, split_y;
        int i;

        /* 2 lines/cols included for the border frame */
        w = widget->cols;

        if (IS_WIDGET_BORDER(widget)) {
            // ERRSYS_INFOPRINT("[%s] paint cols:%d rows:%d\n", widget->name, w, h);
            box(win, 0, 0);
        }

        /* draw title */
        ERRSYS_INFOPRINT("[%s]%s flags %x style %x\n", widget->name, __func__, widget->flags, widget->flags);
        if (IS_WIDGET_TITLE(widget)) {
            split_y = CUI_WIDGET_TITLESPLITTER_COLOFFSET;
            ERRSYS_INFOPRINT("[%s] paint title split line yoffset:%d xoffset:%d\n", widget->name, split_y, 0);
            mvwaddch(win, split_y, 0, ACS_LTEE);
            mvwhline(win, split_y, 1, ACS_HLINE, w - 2);
            mvwaddch(win, split_y, w - 1, ACS_RTEE);
            cui_print(win, split_y - 1, 1, w - 2, widget->title, WHTBLK, CUI_PRINT_ALIGNMENT_MIDDLE);
        }
        ERRSYS_INFOPRINT("%s nfields %d %d\n", __func__, cform->nfields);
        post_form(cform->form);
        /* post_from先完成form绘制，再绘制label文字。否则，form绘制会覆盖label文字 */
        for (i = 0;i < cform->nfields;i ++) {
            struct cui_field_st *cfield = &cform->cfields[i];
            cui_print(cform->win, cfield->beginy_label, cfield->beginx_label, cfield->width_label, cfield->name, WHTBLK, CUI_PRINT_ALIGNMENT_MIDDLE);
        }
        wrefresh(win);
        cui_dump_form(widget);
        // 移动到form field的文字内容结尾
        form_driver(cform->form, REQ_END_FIELD);
    }
}

struct cui_widget_operations __form_ops = {
    .name = "form",
    .parse = cui_form_parse,
    .create = cui_form_create,
    .destroy = cui_form_destroy,
    .paint = cui_form_paint,
    .keyevent = cui_form_keyevent,
};